﻿// ------------------------------------------------------------------------
// 版权信息
// 版权归重庆虫儿飞科技有限公司所有。
// 所有权利保留。
// 官方网站：https://netokit.com
// 许可证信息
// Neto.Kit 项目主要遵循 MIT 许可证和 Apache 许可证（版本 2.0）进行分发和使用。
// 许可证的完整文本可以在源代码树根目录中的 LICENSE-APACHE 和 LICENSE-MIT 文件中找到。
// 
// 使用条款
// 使用本代码应遵守相关法律法规和许可证的要求。
// 
// 免责声明
// 对于因使用本代码而产生的任何直接、间接、偶然、特殊或后果性损害，我们不承担任何责任。
// 
// 其他重要信息
// Neto.Kit 项目的版权、商标、专利和其他相关权利均受相应法律法规的保护。
// 有关 Neto.Kit 项目的其他详细信息，请参阅位于源代码树根目录中的 COPYRIGHT 和 DISCLAIMER 文件。
// 
// 更多信息
// 请访问 https://netokit.com 获取更多关于 Neto.Kit 项目的许可证和版权信息。
// ------------------------------------------------------------------------

#region

using System.Net.Mail;
using System.Text.RegularExpressions;
using System.Web;

#endregion

namespace Neto.Extensions;

public static class StringExtensions
{
    /// <summary>
    ///     处理经过Xss过滤的内容
    /// </summary>
    /// <param name="str">输入字符串</param>
    /// <returns>处理后的字符串</returns>
    public static string ReplaceXssString(this string str)
    {
        if (string.IsNullOrEmpty(str)) return str;

        if (str.Contains("&amp;")) str = str.Replace("&amp;", "&");
        return str;
    }


    /// <summary>
    ///     扩展Trim方法，去除字符串两端指定的字符
    /// </summary>
    /// <param name="str">字符串</param>
    /// <param name="trimStr">需要去除的字符串</param>
    /// <returns>去除指定字符后的字符串</returns>
    public static string Trim(this string str, string trimStr)
    {
        return str.Trim(trimStr?.Trim()?.ToCharArray());
    }

    /// <summary>
    ///     扩展TrimEnd方法，去除字符串末尾指定的字符
    /// </summary>
    /// <param name="str">字符串</param>
    /// <param name="trimStr">末尾需要去除的字符串</param>
    /// <returns>去除指定字符后的字符串</returns>
    public static string TrimEnd(this string str, string trimStr)
    {
        return str.TrimEnd(trimStr?.Trim()?.ToCharArray());
    }

    /// <summary>
    ///     扩展TrimStart方法，去除字符串开头指定的字符
    /// </summary>
    /// <param name="str">字符串</param>
    /// <param name="trimStr">字符串开始需要去除的字符串</param>
    /// <returns>去除指定字符后的字符串</returns>
    public static string TrimStart(this string str, string trimStr)
    {
        return str.TrimStart(trimStr?.Trim()?.ToCharArray());
    }

    /// <summary>
    ///     检查字符串最大长度，返回指定长度的串
    /// </summary>
    /// <param name="input">输入字符串</param>
    /// <param name="maxLength">最大长度</param>
    /// <returns>返回值</returns>
    public static string Split(this string input, int maxLength)
    {
        if (!string.IsNullOrEmpty(input))
        {
            input = input.Trim();
            if (input.Length > maxLength) //按最大长度截取字符串
                input = input[..maxLength];
        }

        return input;
    }

    /// <summary>
    ///     字符串拆分转 object[]
    /// </summary>
    /// <param name="str">输入字符串</param>
    /// <param name="trimStr">默认英文逗号隔开</param>
    /// <returns>拆分后的对象数组</returns>
    public static object[] ToSplitObjects(this string str, string trimStr = ",")
    {
        var list = str.Split(trimStr.ToArray());
        var result = new object[list.Length];
        for (var i = 0; i < list.Length; i++) result[i] = list[i];
        return result;
    }

    /// <summary>
    ///     转全角的函数(SBC case)
    /// </summary>
    /// <param name="str">输入字符串</param>
    /// <returns>全角字符串</returns>
    public static string ToSbc(this string str)
    {
        var c = str.ToCharArray();
        for (var i = 0; i < c.Length; i++)
        {
            if (c[i] == 32)
            {
                c[i] = (char)12288;
                continue;
            }

            if (c[i] < 127)
                c[i] = (char)(c[i] + 65248);
        }

        return new string(c);
    }

    /// <summary>
    ///     转半角的函数(SBC case)
    /// </summary>
    /// <param name="str">输入字符串</param>
    /// <returns>半角字符串</returns>
    public static string ToDbc(this string str)
    {
        var c = str.ToCharArray();
        for (var i = 0; i < c.Length; i++)
        {
            if (c[i] == 12288)
            {
                c[i] = (char)32;
                continue;
            }

            if (c[i] > 65280 && c[i] < 65375)
                c[i] = (char)(c[i] - 65248);
        }

        return new string(c);
    }


    /// <summary>
    ///     移除字符串末尾指定字符
    /// </summary>
    /// <param name="str">需要移除的字符串</param>
    /// <param name="removeString">移除的指定字符</param>
    /// <returns>移除后的字符串</returns>
    public static string RemoveLastStr(this string str, string removeString)
    {
        if (string.IsNullOrWhiteSpace(str) || string.IsNullOrWhiteSpace(removeString))
            return str;

        var str2 = str.LastIndexOf(removeString, StringComparison.OrdinalIgnoreCase);
        return str2 != -1 ? str[..str2] : str;
    }


    /// <summary>
    ///     移除字符串开始处指定的字符或子字符串。
    /// </summary>
    /// <param name="str">需要进行处理的原始字符串。</param>
    /// <param name="removeStr">需要从原始字符串开始处移除的字符或子字符串。</param>
    /// <returns>处理后的字符串。</returns>
    public static string RemoveStartStr(this string str, string removeStr)
    {
        if (string.IsNullOrWhiteSpace(str) || string.IsNullOrWhiteSpace(removeStr))
            return str;

        return str.StartsWith(removeStr, StringComparison.OrdinalIgnoreCase) ? str[removeStr.Length..] : str;
    }


    ///// <summary>
    ///// 在字符串末尾，换行添加内容
    ///// </summary>
    ///// <param name="Str"></param>
    ///// <param name="appendstr"></param>
    //public static string AppendLine(this string Str, string appendstr) => $"{Str}\r\n{appendstr}";


    /// <summary>
    ///     执行cmd命令
    ///     多命令请使用批处理命令连接符：
    ///     <![CDATA[
    /// &:同时执行两个命令
    /// |:将上一个命令的输出,作为下一个命令的输入
    /// &&：当&&前的命令成功时,才执行&&后的命令
    /// ||：当||前的命令失败时,才执行||后的命令]]>
    ///     其他请百度
    /// </summary>
    /// <param name="cmd"></param>
    public static string RunCmd(this string cmd)
    {
        cmd = cmd.Trim().TrimEnd('&') + "&exit"; ////说明：不管命令是否成功均执行exit命令，否则当调用ReadToEnd()方法时，会处于假死状态
        using var _process = new Process();
        _process.StartInfo.FileName = "cmd.exe";
        _process.StartInfo.UseShellExecute = false; //是否使用操作系统shell启动
        _process.StartInfo.RedirectStandardInput = true; //接受来自调用程序的输入信息
        _process.StartInfo.RedirectStandardOutput = true; //由调用程序获取输出信息
        _process.StartInfo.RedirectStandardError = true; //重定向标准错误输出
        _process.StartInfo.CreateNoWindow = true; //不显示程序窗口
        _process.Start(); //启动程序

        //向cmd窗口写入命令
        _process.StandardInput.WriteLine(cmd);
        _process.StandardInput.AutoFlush = true;

        //获取cmd窗口的输出信息
        var output = _process.StandardOutput.ReadToEnd();
        _process.WaitForExit(); //等待程序执行完退出进程
        _process.Close();

        //使用示例
        /*
        使用示例

        示例1：显示ipconfig信息

        string cmd =@"ipconfig/all";
        示例2：打开VS2010命令提示

        string cmd =@"C:&cd C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC&vcvarsall.bat";
        示例3：使用sn.exe工具产生密钥对并显示

        string cmd =@"C:&cd C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC&vcvarsall.bat&sn -k d:          \LicBase.snk&sn   -p d:\LicBase.snk d:\LicBasePubKey.snk&sn -tp d:\LicBasePubKey.snk";
        调用

        string output = "";
        CmdHelper.RunCmd(cmd, out output);
        MessageBox.Show(output);

         */
        return output;
    }


    /// <summary>
    ///     字符串掩码
    ///     手机号邮箱等隐私信息进行自定义字符打码
    /// </summary>
    /// <param name="s">字符串</param>
    /// <param name="mask">掩码符</param>
    /// <param name="start">掩码开始位置</param>
    /// <param name="end">掩码结束位置（不包含）,如果end位置大于字符串实际长度，默认未字符串长度</param>
    /// <returns>掩码后的字符串</returns>
    public static string Mask(this string s, char mask = '*', int start = 3, int? end = null)
    {
        if (string.IsNullOrWhiteSpace(s?.Trim())) return s;
        s = s.Trim();

        // 如果end为null，则默认为字符串末尾
        end ??= s.Length;

        // 确保end在有效范围内
        if (end.Value > s.Length) end = s.Length;

        // 确保start和end在有效范围内
        if (start < 0 || start >= end) throw new ArgumentException("Invalid start or end index for masking.");

        var prefix = s[..start];
        var suffix = s[end.Value..];
        var maskedPart = new string(mask, end.Value - start);

        return prefix + maskedPart + suffix;
    }

    /// <summary>
    ///     html解码
    /// </summary>
    /// <param name="content">内容</param>
    /// <returns></returns>
    public static string HtmlDecode(this string content)
    {
        return HttpUtility.HtmlDecode(content);
    }

    /// <summary>
    ///     html编码
    /// </summary>
    /// <param name="content">内容</param>
    /// <returns></returns>
    public static string HtmlEncode(this string content)
    {
        return HttpUtility.HtmlEncode(content);
    }

    /// <summary>
    ///     移除字符串中的所有空格以及制表符、换页符等
    /// </summary>
    /// <param name="content"></param>
    /// <returns></returns>
    public static string RemoveBlankSpace(this string content)
    {
        if (string.IsNullOrWhiteSpace(content)) return content;
        //正则表达式方法Regex.Replace()和匹配符\s(匹配任何空白字符，包括空格，制表符，换页符等，与[\f\n\t\r\v]等效)
        return Regex.Replace(content, @"\s", "");
    }

    /// <summary>
    ///     字符串截取按字节长度
    /// </summary>
    /// <param name="content">待截取的字符串</param>
    /// <param name="maxByteLength">最大保留字节长度</param>
    /// <returns>截取后的字符串</returns>
    private static string TruncateByByte(string content, int maxByteLength)
    {
        // 检查输入参数的有效性
        if (string.IsNullOrWhiteSpace(content)) return "";
        if (maxByteLength < 1) return content; // 无效的byteLength值直接返回原值

        var encoding = Encoding.UTF8;
        var bytes = encoding.GetBytes(content);

        // 如果原始字节长度小于等于目标长度，直接返回原始字符串，避免不必要的处理
        if (bytes.Length <= maxByteLength) return content;

        string result;
        try
        {
            // 修改此处以避免创建不必要的字节数组拷贝，直接使用Span或Memory结构提高性能
            using var memory = new MemoryStream(bytes);
            memory.Position = 0; // 重置流位置

            // 读取目标长度的字节数据到Span
            Span<byte> truncatedBytes = stackalloc byte[maxByteLength]; // 使用stackalloc减少GC压力
            var read = memory.Read(truncatedBytes);

            result = encoding.GetString(truncatedBytes);
        }
        catch (Exception e)
        {
            // 异常处理，如读取错误等
            Console.WriteLine($"Error processing bytes: {e.Message}");
            return content; // 在异常情况下返回原字符串
        }

        // 特殊情况处理：检查截取后的字符串长度是否符合预期
        if (encoding.GetByteCount(result) > maxByteLength)
            // 如果存在多字节字符被截断，则尝试去除最后一个字符
            result = result.Remove(result.Length - 1);

        return result;
    }

    /// <summary>
    ///     检查字符串是否为有效的电子邮件地址
    /// </summary>
    /// <param name="str">字符串</param>
    /// <returns>是否为有效的电子邮件地址</returns>
    public static bool IsValidEmail(this string str)
    {
        try
        {
            var addr = new MailAddress(str);
            return addr.Address == str;
        }
        catch
        {
            return false;
        }
    }

    /// <summary>
    ///     检查字符串是否为有效的URL
    /// </summary>
    /// <param name="str">字符串</param>
    /// <returns>是否为有效的URL</returns>
    public static bool IsValidUrl(this string str)
    {
        var result = Uri.TryCreate(str, UriKind.Absolute, out var uriResult)
                     && (uriResult?.Scheme == Uri.UriSchemeHttp || uriResult?.Scheme == Uri.UriSchemeHttps);
        return result;
    }

    /// <summary>
    ///     将字符串转换为Base64编码
    /// </summary>
    /// <param name="str">字符串</param>
    /// <returns>Base64编码后的字符串</returns>
    public static string ToBase64(this string str)
    {
        var bytes = Encoding.UTF8.GetBytes(str);
        return Convert.ToBase64String(bytes);
    }

    /// <summary>
    ///     将Base64编码的字符串解码为原始字符串
    /// </summary>
    /// <param name="str">Base64编码的字符串</param>
    /// <returns>解码后的字符串</returns>
    public static string FromBase64(this string str)
    {
        var bytes = Convert.FromBase64String(str);
        return Encoding.UTF8.GetString(bytes);
    }
}